﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;

namespace Pacol
{
    public class Player
    {
        PlayerIndex _playerIndex;

        //the event use to fire a missile
        public delegate void FireEventHandler(Game1.Direction direction, Player player);
        public event FireEventHandler FireEvent;

        //the event use to update the player
        public delegate void MoveEventHandler(Game1.Direction direction, Player player);
        public event MoveEventHandler MoveEvent;

        //the event use to update the player
        public delegate void NumberPlayerEventHandler();
        public event NumberPlayerEventHandler NumberPlayer;

        //Position of the player
        public double[] _position = new double[2];
        public int[] _positionTarget = new int[2];

        public int _score = 0;

        //Direction of the player
        public Game1.Direction _direction;

        //Flag to fire with the player
        public bool _canFire = true;
        public int _animation = 0;

        //Color of the player
        public Color _color;

        public bool _move = false;
        public bool _dead = false;

        public Timer timerEnableFire = new Timer();

        private double _elapsedTime;
        private double _speed;
        private Labyrinth _labyrinth;

        private Timer timerSpeed = new Timer(100);
        private bool _canChangeSpeed = true;

        public Player(Color color, Labyrinth labyrinth, PlayerIndex playerIndex, int indexBlocX, int indexBlocY, int blockSize, int score =0)
        {
            //Direction by default
            _direction = Game1.Direction.down;

            //Color by default
            _color = color;

            _score = score;

            _labyrinth = labyrinth;

            //Position by default
            _position[Game1.x] = indexBlocX * blockSize;
            _position[Game1.y] = indexBlocY * blockSize;
            _positionTarget[Game1.x] = (int)_position[Game1.x];
            _positionTarget[Game1.y] = (int)_position[Game1.y];

            //timer to refresh the animation
            Timer timer = new Timer(200);
            timer.Elapsed += timer_Elapsed;
            timer.Start();

            //timer to enable the fire
            timerEnableFire = new Timer(200);
            timerEnableFire.Elapsed += timerEnableFire_Elapsed;

            timerSpeed.Elapsed += timerSpeed_Elapsed;

            _speed = Game1._blockSize * 0.002;

            _playerIndex = playerIndex;
        }

        void timerSpeed_Elapsed(object sender, ElapsedEventArgs e)
        {
            _canChangeSpeed = true;
            timerSpeed.Stop();
        }

        void timerEnableFire_Elapsed(object sender, ElapsedEventArgs e)
        {
            _canFire = true;
            timerEnableFire.Stop();
        }

        /// <summary>
        /// Update of the player animation
        /// </summary>
        void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (!_move || _dead)
                return;
            _animation += 1;
            _animation = _animation % 2;
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public void Update(GameTime gameTime)
        {
            if (_dead)
                return;

            bool move = false;
            Game1.Direction direction = Game1.Direction.down;
            _elapsedTime = gameTime.ElapsedGameTime.TotalMilliseconds;

            //Check if the player want to move
            if (Keyboard.GetState().IsKeyDown(Keys.Up) || GamePad.GetState(_playerIndex).DPad.Up == ButtonState.Pressed)
            {
                direction = Game1.Direction.up;
                move = true;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Down) || GamePad.GetState(_playerIndex).DPad.Down == ButtonState.Pressed)
            {
                direction = Game1.Direction.down;
                move = true;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Left) || GamePad.GetState(_playerIndex).DPad.Left == ButtonState.Pressed)
            {
                direction = Game1.Direction.left;
                move = true;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Right) || GamePad.GetState(_playerIndex).DPad.Right == ButtonState.Pressed)
            {
                direction = Game1.Direction.right;
                move = true;
            }
            if ((Keyboard.GetState().IsKeyDown(Keys.Space) || GamePad.GetState(_playerIndex).Buttons.A == ButtonState.Pressed) && _canFire)
            {
                //one shot every 50 millisecond
                FireEvent(_direction, this);
                _canFire = false;
                timerEnableFire.Start();
            }
            if (GamePad.GetState(_playerIndex).Buttons.Start == ButtonState.Pressed)
            {
                NumberPlayer();
            }
            if (Keyboard.GetState().IsKeyDown(Keys.S) || GamePad.GetState(_playerIndex).Buttons.B == ButtonState.Pressed)
            {
                if(_canChangeSpeed)
                {
                    if (Enemy._accelerated)
                    {
                        Enemy._accelerated = false;
                        Enemy._speedParam = Enemy._speedParam / 10;
                    }
                    else
                    {
                        Enemy._accelerated = true;
                        Enemy._speedParam = Enemy._speedParam * 10;
                    }
                    _canChangeSpeed = false;
                    timerSpeed.Start();
                }
            }

            //look in the labyrinth if the move is possible
            if(move && ! _move)
                MoveEvent(direction, this);
            
            //move the player
            if (_position[Game1.x] != _positionTarget[Game1.x] || _position[Game1.y] != _positionTarget[Game1.y])
                _move = true;
            else
                _move = false;

            if (_move)
            {
                //on the left of the target
                if (_position[Game1.x] < _positionTarget[Game1.x])
                {
                    _position[Game1.x] += _elapsedTime * _speed;

                    if (_position[Game1.x] > _positionTarget[Game1.x])
                        _position[Game1.x] = _positionTarget[Game1.x];
                }
                //on the right of the target
                else if (_position[Game1.x] > _positionTarget[Game1.x])
                {
                    _position[Game1.x] -= _elapsedTime * _speed;

                    if (_position[Game1.x] < _positionTarget[Game1.x])
                        _position[Game1.x] = _positionTarget[Game1.x];
                }
                //on the top of the target
                else if (_position[Game1.y] < _positionTarget[Game1.y])
                {
                    _position[Game1.y] += _elapsedTime * _speed;

                    if (_position[Game1.y] > _positionTarget[Game1.y])
                        _position[Game1.y] = _positionTarget[Game1.y];
                }
                //on the bottom of the target
                else if (_position[Game1.y] > _positionTarget[Game1.y])
                {
                    _position[Game1.y] -= _elapsedTime * _speed;

                    if (_position[Game1.y] < _positionTarget[Game1.y])
                        _position[Game1.y] = _positionTarget[Game1.y];
                }
            }

            if (IsBlocked())
            {
                if(_color.Equals(Game1._color[0]))
                    _color = Game1._color[1];
                else
                    _color = Game1._color[0];
            }
        }

        /// <summary>
        /// This is called when the player should draw itself.
        /// </summary>
        /// <param name="blocSize">size of all the bloc (with wall)</param>
        public void Draw(SpriteBatch spriteBatch, int blocSize)
        {
            //animation dead
            if(_dead)
                spriteBatch.Draw(Game1._wizardTex[Game1._wizardDress], new Rectangle((int)_position[Game1.x] + Game1._thicknessBorder, (int)_position[Game1.y] + Game1._thicknessBorder, blocSize - (2 * Game1._thicknessBorder), blocSize - (2 * Game1._thicknessBorder)), new Rectangle(0, (32 * 4), 32, 32), Color.White);

            //animation run
            else if(_canFire)
                spriteBatch.Draw(Game1._wizardTex[Game1._wizardDress], new Rectangle((int)_position[Game1.x] + Game1._thicknessBorder, (int)_position[Game1.y] + Game1._thicknessBorder, blocSize - (2 * Game1._thicknessBorder), blocSize - (2 * Game1._thicknessBorder)), new Rectangle((32 * this._animation), (32 * (int)this._direction), 32, 32), Color.White);

            //animation fire
            else
                spriteBatch.Draw(Game1._wizardTex[Game1._wizardDress], new Rectangle((int)_position[Game1.x] + Game1._thicknessBorder, (int)_position[Game1.y] + Game1._thicknessBorder, blocSize - (2 * Game1._thicknessBorder), blocSize - (2 * Game1._thicknessBorder)), new Rectangle(64, (32 * (int)this._direction), 32, 32), Color.White);
        }

        /// <summary>
        /// Return the rectangle where is placed the player.
        /// </summary>
        /// <returns></returns>
        public Rectangle getRect()
        {
            return new Rectangle((int)_position[Game1.x] + _labyrinth.blockSize / 2, (int)_position[Game1.y] + _labyrinth.blockSize / 2, _labyrinth.blockSize / 10, _labyrinth.blockSize / 10);
        }

        public Color color
        {
            set { color = value; }
        }

        // test if the player is jailed in a block
        private bool IsBlocked()
        {
            Block currentBlock = _labyrinth.GetBlockAt((int)_position[0], (int)_position[1]);

            if ((currentBlock.colorTop != _color || currentBlock.y == 0) &&
                (currentBlock.colorRight != _color || currentBlock.x == (_labyrinth.width - 1) * _labyrinth.blockSize) &&
                (currentBlock.colorLeft != _color || currentBlock.x == 0) &&
                (currentBlock.colorBottom != _color || currentBlock.y == (_labyrinth.height - 1) * _labyrinth.blockSize))
                return true;
            else
                return false;
        }
    }
}
